package com.minglead.springboot.common.crud;

import java.beans.PropertyEditorSupport;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.validation.ConstraintViolationException;
import javax.validation.ValidationException;
import javax.validation.Validator;

import org.apache.commons.lang3.StringEscapeUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.shiro.authc.AuthenticationException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.ui.Model;
import org.springframework.validation.BindException;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import com.minglead.springboot.common.beanvalidator.BeanValidators;
import com.minglead.springboot.common.mapper.JsonMapper;
import com.minglead.springboot.common.persistence.Page;
import com.minglead.springboot.common.utils.DateUtils;
import com.minglead.springboot.platform.sys.security.SystemAuthorizingRealm.Principal;
import com.minglead.springboot.platform.sys.utils.UserUtils;
/**
 * 控制器支持类
 */
public abstract class BaseController {

	/**
	 * 日志对象
	 */
	protected Logger logger = LoggerFactory.getLogger(getClass());

	/**
	 * 管理基础路径
	 */
	@Value("${adminPath}")
	protected String adminPath;
	
	@Value("${server.servlet.path}")
	protected String contextPath;
	
	/**
	 * freemarker基础路径
	 */
	@Value("${freemarkerPath}")
	protected String freemarkerPath;
	
	
	/**
	 * 前端基础路径
	 */
	@Value("${frontPath}")
	protected String frontPath;
	
	/**
	 * 验证Bean实例对象
	 */
	@Autowired
	protected Validator validator;

	/**
	 * 服务端参数有效性验证
	 * @param object 验证的实体对象
	 * @param groups 验证组
	 * @return 验证成功：返回true；严重失败：将错误信息添加到 message 中
	 */
	protected boolean beanValidator(Model model, Object object, Class<?>... groups) {
		try{
			BeanValidators.validateWithException(validator, object, groups);
		}catch(ConstraintViolationException ex){
			List<String> list = BeanValidators.extractPropertyAndMessageAsList(ex, ": ");
			list.add(0, "数据验证失败：");
			addMessage(model, list.toArray(new String[]{}));
			return false;
		}
		return true;
	}
	
	@SuppressWarnings("rawtypes")
	public void renderGrid(HttpServletRequest request, HttpServletResponse response,Page page){
		Map<String, Object> map = new HashMap<String, Object>();
		Integer pageNo=page.getPageNo();
		Integer pageSize=page.getPageSize();
		int totalRecord = (int) page.getCount(); // 总记录数
		int totalPage = totalRecord % pageSize == 0 ? totalRecord/ pageSize: totalRecord / pageSize+ 1; // 计算总页数
		map.put("page", pageNo);
		map.put("total", totalPage);
		map.put("records", totalRecord);
		map.put("rows", page.getList());
		renderString(response, JsonMapper.toJsonString(map), "application/json");
	}
	
	/**
	 * 服务端参数有效性验证
	 * @param object 验证的实体对象
	 * @param groups 验证组
	 * @return 验证成功：返回true；严重失败：将错误信息添加到 flash message 中
	 */
	protected boolean beanValidator(RedirectAttributes redirectAttributes, Object object, Class<?>... groups) {
		try{
			BeanValidators.validateWithException(validator, object, groups);
		}catch(ConstraintViolationException ex){
			List<String> list = BeanValidators.extractPropertyAndMessageAsList(ex, ": ");
			list.add(0, "数据验证失败：");
			addMessage(redirectAttributes, list.toArray(new String[]{}));
			return false;
		}
		return true;
	}
	
	/**
	 * 服务端参数有效性验证
	 * @param object 验证的实体对象
	 * @param groups 验证组，不传入此参数时，同@Valid注解验证
	 * @return 验证成功：继续执行；验证失败：抛出异常跳转400页面。
	 */
	protected void beanValidator(Object object, Class<?>... groups) {
		BeanValidators.validateWithException(validator, object, groups);
	}
	
	/**
	 * 添加Model消息
	 * @param message
	 */
	protected void addMessage(Model model, String... messages) {
		StringBuilder sb = new StringBuilder();
		for (String message : messages){
			sb.append(message).append(messages.length>1?"<br/>":"");
		}
		model.addAttribute("message", sb.toString());
	}
	
	/**
	 * 添加Flash消息
	 * @param message
	 */
	protected void addMessage(RedirectAttributes redirectAttributes, String... messages) {
		StringBuilder sb = new StringBuilder();
		for (String message : messages){
			sb.append(message).append(messages.length>1?"<br/>":"");
		}
		redirectAttributes.addFlashAttribute("message", sb.toString());
	}
	
	/**
	 * 客户端返回字符串
	 * @param response
	 * @param string
	 * @return
	 */
	protected String renderString(HttpServletResponse response, String string, String type) {
		try {
			response.reset();
	        response.setContentType(type);
	        response.setCharacterEncoding("utf-8");
			response.getWriter().print(string);
			return null;
		} catch (IOException e) {
			return null;
		}
	}
	
	/**
	 * 响应字符串 用于ajax请求响应
	 * @param str
	 * @throws Exception
	 */
	public void responseText(String str,HttpServletResponse reponse) {
		try {
			reponse.setContentType("text/html;charset=utf-8");
			PrintWriter out = reponse.getWriter();
			out.write(str);
			out.flush();
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}	 
	

	/**
	 * json格式返回
	 * @param obj
	 * @param response
	 * @throws Exception
	 */
	public void responseObject(Object obj,HttpServletResponse response){
		try {
			String str = JsonMapper.toJsonString(obj);
			response.setContentType("application/json;charset=utf-8");
			PrintWriter out = response.getWriter();
			out.write(str);
			out.flush();
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}

	public void responseHtml(Object obj,HttpServletResponse response){
		try {
			String str = JsonMapper.toJsonString(obj);
			response.setContentType("text/html;charset=utf-8");
			PrintWriter out = response.getWriter();
			out.write(str);
			out.flush();
			out.close();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
	}
	
	
	/**
	 * 参数绑定异常
	 */
	@ExceptionHandler({BindException.class, ConstraintViolationException.class, ValidationException.class})
    public String bindException() {  
        return "error/400";
    }
	
	/**
	 * 授权登录异常
	 */
	@ExceptionHandler({AuthenticationException.class})
    public String authenticationException() {  
        return "error/403";
    }

	@RequestMapping(value = "error")
    public String error() {  
        return "error/eee";
    }
	
	
	/**
	 * 初始化数据绑定
	 * 1. 将所有传递进来的String进行HTML编码，防止XSS攻击
	 * 2. 将字段中Date类型转换为String类型
	 */
	@InitBinder
	protected void initBinder(WebDataBinder binder) {
		// String类型转换，将所有传递进来的String进行HTML编码，防止XSS攻击
		binder.registerCustomEditor(String.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) {
				setValue(text == null ? null : StringEscapeUtils.escapeHtml4(text.trim()));
			}
			@Override
			public String getAsText() {
				Object value = getValue();
				return value != null ? value.toString() : "";
			}
		});
		// Date 类型转换
		binder.registerCustomEditor(Date.class, new PropertyEditorSupport() {
			@Override
			public void setAsText(String text) {
				setValue(DateUtils.parseDate(text));
			}
//			@Override
//			public String getAsText() {
//				Object value = getValue();
//				return value != null ? DateUtils.formatDateTime((Date)value) : "";
//			}
		});
	}
	
	/**
	 * 是否是Ajax异步请求
	 * @param request
	 */
	public static boolean isAjaxRequest(HttpServletRequest request){
		
		String accept = request.getHeader("accept");
		String xRequestedWith = request.getHeader("X-Requested-With");
		Principal principal = UserUtils.getPrincipal();
		// 如果是异步请求或是手机端，则直接返回信息
		return ((accept != null && accept.indexOf("application/json") != -1 
			|| (xRequestedWith != null && xRequestedWith.indexOf("XMLHttpRequest") != -1)
			|| (principal != null && principal.isMobileLogin())));
	}
	
	/**
	 * execl 文件下载
	 * @param fileName 下载的文件显示名称
	 * @param wb HSSFWorkbook对象
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	public void downloadExcelFile(String fileName,HSSFWorkbook wb,HttpServletRequest request,HttpServletResponse response) throws  Exception{
	    fileName = new String(fileName.getBytes(),"ISO8859_1");
        response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"", fileName));
		response.setContentType("application/msexcel;charset=UTF-8");
        OutputStream ouputStream = response.getOutputStream();  
        wb.write(ouputStream);  
        ouputStream.flush();  
        ouputStream.close();  
	}
	
	/**
	 * 文件下载
	 * @param file 文件
	 * @param fileName 下载显示的文件名称
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	public void downloadFile(File file,String fileName,HttpServletRequest request,HttpServletResponse response) throws  Exception{
		fileName = StringUtils.isEmpty(fileName)?file.getName():fileName;
	    String _fileName = new String(fileName.getBytes(),"ISO8859_1");
        response.setHeader("Content-disposition", String.format("attachment; filename=\"%s\"", _fileName));
		response.setContentType("application/msexcel;charset=UTF-8"); 
        ServletOutputStream out = response.getOutputStream();
		  BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));  
           byte[] content = new byte[1024];  
           int length;  
           while ((length = bis.read(content, 0, content.length)) != -1) {  
          	 out.write(content, 0, length);  
           }  
		out.flush();
		out.close();
	}
	
	/**
	 * 图片预览
	 * @param file
	 * @param request
	 * @param response
	 * @throws Exception
	 */
	public void downloadImgFile(File file,HttpServletRequest request,HttpServletResponse response) throws  Exception{
		response.setContentType("image/jpeg;charset=UTF-8"); 
        ServletOutputStream out = response.getOutputStream();
		  BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));  
           byte[] content = new byte[1024];  
           int length;  
           while ((length = bis.read(content, 0, content.length)) != -1) {  
          	 out.write(content, 0, length);  
           }  
		out.flush();
		out.close();
	}
	
	
}
